//////////////////////////////////////////////
// File				:	Input.cpp
//
// Author			:	Nick Faletra (NF)
//
// Date Created		:	12/22/2013
//////////////////////////////////////////////
#include "Input.h"

#include "Keyboard.h"
#include "Mouse.h"
#include "Joystick.h"

CInput::CInput() : m_pDI(nullptr)
				 , m_pKeyboard(nullptr)
				 , m_pMouse(nullptr)
{
}

CInput::~CInput()
{
}

CInput* CInput::GetInstance()
{
	return &m_Instance;
}

bool CInput::CheckJoyNum(int nJoyNum) const
{
	return nJoyNum < 0 || nJoyNum > m_vpJoysticks.size();
}

bool CInput::Initialize(HWND hWnd, HINSTANCE hInstance, unsigned int initDevices, unsigned int exclusiveDevices)
{
	if (FAILED(DirectInput8Create(hInstance, DIRECTINPUT_VERSION, IID_IDirectInput8, (void**)&m_pDI, nullptr)))
		return false;

	if (initDevices & DI_KEYBOARD)
		if (!InitKeyboard(hWnd, (exclusiveDevices & DI_KEYBOARD) ? true : false))
			return false;

	if (initDevices & DI_MOUSE)
		if (!InitMouse(hWnd, (exclusiveDevices & DI_MOUSE) ? true : false))
			return false;

	if (initDevices & DI_JOYSTICKS)
		if (!InitJoysticks(hWnd, (exclusiveDevices & DI_JOYSTICKS) ? true : false))
			return false;

	return true;
}

void CInput::Shutdown()
{
	UnacquireAll();

	if (m_pKeyboard)
		SAFE_DELETE(m_pKeyboard);

	if (m_pMouse)
		SAFE_DELETE(m_pMouse);

	if (m_vpJoysticks.size() > 0)
	{
		for (unsigned int i = 0; i < m_vpJoysticks.size(); ++i)
			SAFE_DELETE(m_vpJoysticks[i]);

		m_vpJoysticks.clear();
	}
}

void CInput::ReadDevices()
{
	if (m_pKeyboard)
	{
		m_pKeyboard->ReadDevice();
		m_pKeyboard->ReadBufferedDevice();
	}

	if (m_pMouse)
	{
		m_pMouse->ReadDevice();
		m_pMouse->ReadBufferedDevice();
	}
	
	for (unsigned int i = 0; i < m_vpJoysticks.size(); ++i)
	{
		if (m_vpJoysticks[i])
		{
			m_vpJoysticks[i]->ReadDevice();
			m_vpJoysticks[i]->ReadBufferedDevice();
		}
	}
}

void CInput::AcquireAll()
{
	if (m_pKeyboard)
		m_pKeyboard->Acquire();

	if (m_pMouse)
		m_pMouse->Acquire();

	for (unsigned int i = 0; i < m_vpJoysticks.size(); ++i)
		if (m_vpJoysticks[i])
			m_vpJoysticks[i]->Acquire();
}

void CInput::UnacquireAll()
{
	if (m_pKeyboard)
		m_pKeyboard->Unacquire();

	if (m_pMouse)
		m_pMouse->Unacquire();

	for (unsigned int i = 0; i < m_vpJoysticks.size(); ++i)
		if (m_vpJoysticks[i])
			m_vpJoysticks[i]->Unacquire();
}

bool CInput::InitKeyboard(HWND hWnd, bool bExclusive)
{
	if (m_pKeyboard)
	{
		DIERRBOX(hWnd, "Keyboard has already been initialized");
		return false;
	}

	m_pKeyboard = new CKeyboard(m_pDI, hWnd, bExclusive);

	if (!m_pKeyboard)
		return false;

	return true;
}

bool CInput::KeyDown(unsigned char ucDIKey)
{
	if (!m_pKeyboard)
		return false;

	return m_pKeyboard->KeyDown(ucDIKey);
}

bool CInput::KeyPressed(unsigned char ucDIKey)
{
	if (!m_pKeyboard)
		return false;

	return m_pKeyboard->KeyPressed(ucDIKey);
}

bool CInput::KeyPressedEx(unsigned char ucDIKey)
{
	if (!m_pKeyboard)
		return false;

	return m_pKeyboard->KeyPressedEx(ucDIKey);
}

bool CInput::KeyUp(unsigned char ucDIKey)
{
	if (!m_pKeyboard)
		return false;

	return m_pKeyboard->KeyUp(ucDIKey);
}

bool CInput::KeyReleased(unsigned char ucDIKey)
{
	if (!m_pKeyboard)
		return false;

	return m_pKeyboard->KeyReleased(ucDIKey);
}

bool CInput::KeyReleasedEx(unsigned char ucDIKey)
{
	if (!m_pKeyboard)
		return false;

	return m_pKeyboard->KeyReleasedEx(ucDIKey);
}

char CInput::CheckKeys()
{
	return m_pKeyboard ? m_pKeyboard->CheckKeys() : 0;
}

char CInput::CheckBufferedKeysEx()
{
	return m_pKeyboard ? m_pKeyboard->CheckBufferedKeysEx() : 0;
}

unsigned char CInput::GetDIKCode()
{
	return m_pKeyboard ? m_pKeyboard->GetDIKCode() : 0;
}

unsigned char CInput::GetBufferedDIKCodeEx()
{
	return m_pKeyboard ? m_pKeyboard->GetBufferedDIKCodeEx() : 0;
}

bool CInput::InitMouse(HWND hWnd, bool bExclusive)
{
	if (m_pMouse)
	{
		DIERRBOX(hWnd, "Mouse has already been initialized");
		return false;
	}

	m_pMouse = new CMouse(m_pDI, hWnd, bExclusive);

	if (!m_pMouse)
		return false;

	return true;
}

bool CInput::MouseButtonDown(unsigned char ucButton)
{
	if (!m_pMouse)
		return false;

	return m_pMouse->ButtonDown(ucButton);
}

bool CInput::MouseButtonPressed(unsigned char ucButton)
{
	if (!m_pMouse)
		return false;

	return m_pMouse->ButtonPressed(ucButton);
}

bool CInput::MouseButtonPressedEx(unsigned char ucButton)
{
	if (!m_pMouse)
		return false;

	return m_pMouse->ButtonPressedEx(ucButton);
}

bool CInput::MouseButtonUp(unsigned char ucButton)
{
	if (!m_pMouse)
		return false;

	return m_pMouse->ButtonUp(ucButton);
}

bool CInput::MouseButtonReleased(unsigned char ucButton)
{
	if (!m_pMouse)
		return false;

	return m_pMouse->ButtonReleased(ucButton);
}

bool CInput::MouseButtonReleasedEx(unsigned char ucButton)
{
	if (!m_pMouse)
		return false;

	return m_pMouse->ButtonReleasedEx(ucButton);
}

int CInput::MouseCheckBufferedButtons()
{
	return m_pMouse ? m_pMouse->CheckBufferedButtons() : -1;
}

int CInput::MouseCheckBufferedButtonsEx()
{
	return m_pMouse ? m_pMouse->CheckBufferedButtonsEx() : -1;
}

long CInput::MouseMovementX() const
{
	return m_pMouse ? m_pMouse->MovementX() : 0;
}

long CInput::MouseMovementY() const
{
	return m_pMouse ? m_pMouse->MovementY() : 0;
}

long CInput::MouseWheelMovement() const
{
	return m_pMouse ? m_pMouse->WheelMovement() : 0;
}

int CInput::MouseGetNumButtons() const
{
	return m_pMouse ? m_pMouse->GetNumButtons() : 0;
}

int CInput::MouseGetPosX() const
{
	return m_pMouse ? m_pMouse->GetPosX() : 0;
}

int CInput::MouseGetPosY() const
{
	return m_pMouse ? m_pMouse->GetPosY() : 0;
}

void CInput::MouseSetPosX(const int nPosX)
{
	if (m_pMouse)
		m_pMouse->SetPosX(nPosX);
}

void CInput::MouseSetPosY(const int nPosY)
{
	if (m_pMouse)
		m_pMouse->SetPosY(nPosY);
}

bool CInput::InitJoysticks(HWND hWnd, bool bExclusive)
{
	if (m_vpJoysticks.size())
	{
		DIERRBOX(hWnd, "Joysticks have already been initialized");
		return false;
	}

	tJoystickSetupInfo joySetupInfo = { hWnd, bExclusive };

	if (FAILED(m_pDI->EnumDevices(DI8DEVCLASS_GAMECTRL, EnumJoysticksCallback, (void*)&joySetupInfo, DIEDFL_ATTACHEDONLY)))
		return false;

	if (m_vpJoysticks.size() == 0)
		return false;

	return true;
}

BOOL CALLBACK CInput::EnumJoysticksCallback(const DIDEVICEINSTANCE* lpdidi, void* pVoid)
{
	CInput* pDI = CInput::GetInstance();

	tJoystickSetupInfo joySetupInfo = *(tJoystickSetupInfo*)pVoid;

	CJoystick* pJoystick = new CJoystick(pDI->m_pDI, joySetupInfo.hWnd, lpdidi, joySetupInfo.bExclusive);

	if (!pJoystick)
		return DIENUM_STOP;

	pDI->m_vpJoysticks.push_back(pJoystick);

	return DIENUM_CONTINUE;
}

bool CInput::JoystickButtonDown(unsigned char ucButton, int nJoyNum)
{
	if (CheckJoyNum(nJoyNum))
		return false;

	return m_vpJoysticks[nJoyNum]->ButtonDown(ucButton);
}

bool CInput::JoystickButtonPressed(unsigned char ucButton, int nJoyNum)
{
	if (CheckJoyNum(nJoyNum))
		return false;

	return m_vpJoysticks[nJoyNum]->ButtonPressed(ucButton);
}

bool CInput::JoystickButtonPressedEx(unsigned char ucButton, int nJoyNum)
{
	if (CheckJoyNum(nJoyNum))
		return false;

	return m_vpJoysticks[nJoyNum]->ButtonPressedEx(ucButton);
}

bool CInput::JoystickButtonUp(unsigned char ucButton, int nJoyNum)
{
	if (CheckJoyNum(nJoyNum))
		return false;

	return m_vpJoysticks[nJoyNum]->ButtonUp(ucButton);
}

bool CInput::JoystickButtonReleased(unsigned char ucButton, int nJoyNum)
{
	if (CheckJoyNum(nJoyNum))
		return false;

	return m_vpJoysticks[nJoyNum]->ButtonReleased(ucButton);
}

bool CInput::JoystickButtonReleased(unsigned char ucButton, int nJoyNum)
{
	if (CheckJoyNum(nJoyNum))
		return false;

	return m_vpJoysticks[nJoyNum]->ButtonReleased(ucButton);
}

bool CInput::JoystickDPadDown(int nDir, int nJoyNum)
{
	if (CheckJoyNum(nJoyNum))
		return false;

	return m_vpJoysticks[nJoyNum]->DPadDown(nDir);
}

bool CInput::JoystickDPadPressed(int nDir, int nJoyNum)
{
	if (CheckJoyNum(nJoyNum))
		return false;
	
	return m_vpJoysticks[nJoyNum]->DPadPressed(nDir);
}

bool CInput::JoystickDPadUp(int nDir, int nJoyNum)
{
	if (CheckJoyNum(nJoyNum))
		return false;

	return m_vpJoysticks[nJoyNum]->DPadUp(nDir);
}

bool CInput::JoystickDPadReleased(int nDir, int nJoyNum)
{
	if (CheckJoyNum(nJoyNum))
		return false;

	return m_vpJoysticks[nJoyNum]->DPadReleased(nDir);
}

bool CInput::JoystickGetLStickDirDown(int nDir, int nJoyNum)
{
	if (CheckJoyNum(nJoyNum))
		return false;

	return m_vpJoysticks[nJoyNum]->GetLStickDirDown(nDir);
}

bool CInput::JoystickGetLStickDirPressed(int nDir, int nJoyNum)
{
	if (CheckJoyNum(nJoyNum))
		return false;

	return m_vpJoysticks[nJoyNum]->GetLStickDirDown(nDir);
}

int CInput::JoystickGetLStickXAmount(int nJoyNum)
{
	if (CheckJoyNum(nJoyNum))
		return 0;

	return m_vpJoysticks[nJoyNum]->GetLStickXAmount();
}

int CInput::JoystickGetLStickYAmount(int nJoyNum)
{
	if (CheckJoyNum(nJoyNum))
		return 0;

	return m_vpJoysticks[nJoyNum]->GetLStickYAmount();
}

float CInput::JoystickGetLStickXNormalized(int nJoyNum)
{
	if (CheckJoyNum(nJoyNum))
		return 0.f;

	return m_vpJoysticks[nJoyNum]->GetLStickXNormalized();
}

float CInput::JoystickGetLStickYNormalized(int nJoyNum)
{
	if (CheckJoyNum(nJoyNum))
		return 0.f;

	return m_vpJoysticks[nJoyNum]->GetLStickYNormalized();
}

bool CInput::JoystickGetRStickDirDown(int nDir, int nJoyNum)
{
	if (CheckJoyNum(nJoyNum))
		return false;

	return m_vpJoysticks[nJoyNum]->GetRStickDirDown(nDir);
}

bool CInput::JoystickGetRStickDirPressed(int nDir, int nJoyNum)
{
	if (CheckJoyNum(nJoyNum))
		return false;

	return m_vpJoysticks[nJoyNum]->GetRStickDirPressed(nDir);
}

int CInput::JoystickGetRStickXAmount(int nJoyNum)
{
	if (CheckJoyNum(nJoyNum))
		return 0;

	return m_vpJoysticks[nJoyNum]->GetRStickXAmount();
}

int CInput::JoystickGetRStickYAmount(int nJoyNum)
{
	if (CheckJoyNum(nJoyNum))
		return 0;

	return m_vpJoysticks[nJoyNum]->GetRStickYAmount();
}

float CInput::JoystickGetRStickXNormalized(int nJoyNum)
{
	if (CheckJoyNum(nJoyNum))
		return 0.f;

	return m_vpJoysticks[nJoyNum]->GetRStickXNormalized();
}

float CInput::JoystickGetRStickYNormalized(int nJoyNum)
{
	if (CheckJoyNum(nJoyNum))
		return 0.f;

	return m_vpJoysticks[nJoyNum]->GetRStickYNormalized();
}

int CInput::JoystickGetLTriggerAmount(int nJoyNum)
{
	if (CheckJoyNum(nJoyNum))
		return 0;

	return m_vpJoysticks[nJoyNum]->GetLTriggerAmount();
}

int CInput::JoystickGetRTriggerAmount(int nJoyNum)
{
	if (CheckJoyNum(nJoyNum))
		return 0;

	return m_vpJoysticks[nJoyNum]->GetRTriggerAmount();
}

float CInput::JoystickGetLTriggerNormalized(int nJoyNum)
{
	if (CheckJoyNum(nJoyNum))
		return 0.f;

	return m_vpJoysticks[nJoyNum]->GetLTriggerNormalized();
}

float CInput::JoystickGetRTriggerNormalized(int nJoyNum)
{
	if (CheckJoyNum(nJoyNum))
		return 0.f;

	return m_vpJoysticks[nJoyNum]->GetRTriggerNormalized();
}

int CInput::JoystickCheckBufferedButtons(int nJoyNum)
{
	if (CheckJoyNum(nJoyNum))
		return -1;

	return m_vpJoysticks[nJoyNum]->CheckBufferedButtons();
}

int CInput::JoystickCheckBufferedButtonsEx(int nJoyNum)
{
	if (CheckJoyNum(nJoyNum))
		return -1;

	return m_vpJoysticks[nJoyNum]->CheckBufferedButtonsEx();
}

int CInput::JoystickGetNumButtons(int nJoyNum) const
{
	if (CheckJoyNum(nJoyNum))
		return 0;

	return m_vpJoysticks[nJoyNum]->GetNumButtons();
}

bool CInput::JoystickUnplugged(int nJoyNum) const
{
	if (CheckJoyNum(nJoyNum))
		return true;

	return m_vpJoysticks[nJoyNum]->GetUnplugged();
}

const char* CInput::JoystickGetName(int nJoyNum) const
{
	if (CheckJoyNum(nJoyNum))
		return "Doesn't exist";

	return m_vpJoysticks[nJoyNum]->GetName();
}

void CInput::JoystickSwapRStickAxes(const bool bSwapAxes, int nJoyNum)
{
	if (CheckJoyNum(nJoyNum))
		return;

	m_vpJoysticks[nJoyNum]->SwapRStickAxes(bSwapAxes);
}